//
// Created by benxb on 2021/12/12.
//

#ifndef BENXDB_LINEAR_INDEX_H
#define BENXDB_LINEAR_INDEX_H

#include <cstdint>
#include <string>
#include <fstream>
#include <vector>
#include <catelog/column.h>
#include <common/config.h>
#include <index/linear_index_segment.h>
#include <deque>
#include <unordered_map>

namespace benxdb {

/**
 * Index file format:
 *
 *  Index pair format
 *  ------------------------------
 *  | Key (12) | SegmentId (8) |
 *  -----------------------------
 *  Index file format
 *  ----------------------------------------------------------------
 *  | IndexPair1 (24) | IndexPair2 (24) | ...
 *  ----------------------------------------------------------------
 *
 */

struct LinearIndexPair {
    LinearIndexKey key;
    index_seg_id_t seg_id; // index data file id && tmp page id
};

struct IndexQuery {
    CompareSign comp;
    LinearIndexKey key;

    IndexQuery(CompareSign comp, const LinearIndexKey& key)
            : comp(comp), key(key) {}
};

class LinearIndex {
public:
    LinearIndex(const std::string& table_name, const Column& column);

    ~LinearIndex();

    /**
     * Append new data to create index
     * @param data data
     * @param size data size
     */
    void GenerateIndex(const char* data, int size, record_id_t begin_id);

    std::vector<LinearIndexPair> GetIndexes();

    /**
     * Query indexes according to query phrase
     * @param query query phrases
     * @param res_pairs result index pair
     */
    void QueryIndexes(std::vector<IndexQuery>& query, std::vector<LinearIndexLeafPair>& res_pairs);

    void GetAllLeafIndexPairs(std::vector<LinearIndexLeafPair>& res_pairs);

private:
    /**
     * different page store in different files
     * for index leaf page which is not full, it stores in index.tmp file
     * for index leaf page which is full, it stores in index.data file
     */
    std::fstream index_io_;
    std::string index_file_;
    std::string index_data_dir_;
    LinearIndexKeyType key_type_;
    Column column_; // which column index
    std::vector<LinearIndexPair> indexes_;
    int64_t index_tmp_file_size_;
    // segment index which need split
    std::vector<int> need_split_;
    // index segments
    std::unordered_map<index_seg_id_t, std::shared_ptr<LinearIndexSegment>> segments_;
    // buffered page for insert index
    std::shared_ptr<LinearIndexPage> page_;

    void InitIndexIO();

    void InitIndexDataDIR();

    std::shared_ptr<LinearIndexSegment> GetIndexSegment(int segment_id);

    void AppendIndex(int seg_index, std::vector<LinearIndexLeafPair> &column, int cposL, int cposH);

    /**
     * GenerateIndex main function
     * @param columns need insert pairs
     * @param cposL column begin index
     * @param cposH column end index
     * @param iposL indexes start index
     * @param iposH indexes end index
     */
    void GenerateIndex(std::vector<LinearIndexLeafPair>& columns, int cposL, int cposH, int iposL, int iposH);

    /**
     * split index segment
     * @param seg_index segment index in indexes_
     */
    void SplitSegment(int seg_index);

    /**
     * Write index back into file
     */
    void FlushIndex();

    index_seg_id_t GetNextSegmentId();

    void HandleSplitSegments();

    int FindFirstSegmentIndex(LinearIndexKey value);

    std::shared_ptr<LinearIndexSegment> GetSegmentByIndex(int index);
};

}


#endif //BENXDB_LINEAR_INDEX_H
